#include "NetConnectRequestPacket.h"
#include "NetUtils.h"
#include "NetPacket.h"
#include "NetConstants.h"
#include "NetPacketPool.h"
#include "NetManager.h"
#include <cstring>
#include "spdlog/spdlog.h"

// 完成

namespace LiteNetLib
{
    /*******************************************************************
     * NetConnectRequestPacket
    ********************************************************************/
    NetConnectRequestPacket::NetConnectRequestPacket(int64_t theConnectionTime, uint8_t theConnectionNumber, net_endpoint *theEndpoint, const NetDataReader &reader) :
        connectionTime(theConnectionTime),
        connectionNumber(theConnectionNumber),
        netDataReader(reader)
    {
        targetEndpoint = *theEndpoint;
    }

    NetConnectRequestPacket::~NetConnectRequestPacket()
    {
        if(netDataReader.netPacket != nullptr)
        {
            NetPacketPool::GetInstance().recycleOrDelete(netDataReader.netPacket);
        }
    }

    int32_t NetConnectRequestPacket::GetProtocolId(const char *rawData)
    {
        return NetUtils::ByteToInt32(rawData + 1);
    }

    NetConnectRequestPacket* NetConnectRequestPacket::FromData(NetPacket *packet, net_endpoint *address)
    {
        if(packet->getConnectionNumber() >= NetConstants::MaxConnectionNumber)
        {
            return nullptr;
        }
        int64_t connectionId = NetUtils::ByteToInt64(&(packet->getRawData()[5]));
        uint8_t addrSize = packet->getRawData()[13];

        NetDataReader reader;
        reader.netPacket = packet;
        reader.headerSize = packet->getHeaderSize() + addrSize;

        return new NetConnectRequestPacket(connectionId, packet->getConnectionNumber(), address, reader);
    }


    /*******************************************************************
     * NetConnectAcceptPacket
    ********************************************************************/
    NetPacket *NetConnectAcceptPacket::Make(int64_t connectionId, uint8_t connectionNumber, bool isReusedPeer)
    {
        NetPacket* packet = NetPacketPool::GetInstance().getWithProperty(PacketProperty::ConnectAccept, 0);
        NetUtils::Int64ToByte(&(packet->getRawData()[1]), connectionId);
        packet->getRawData()[9] = connectionNumber;
        packet->getRawData()[10] = (uint8_t)(isReusedPeer ? 1 : 0);
        return packet;
    }



    /*******************************************************************
     * NetConnectRequestPacket
    ********************************************************************/
    ConnectionRequest::ConnectionRequest(int64_t theConnectionId, uint8_t theConnectionNumber, const NetDataReader &reader, net_endpoint *theEndpoint, NetManager *theListerner) :
        connectionTime(theConnectionId),
        connectionNumber(theConnectionNumber),
        netDataReader(reader),
        m_listener(theListerner),
        m_used(false) 
    {
        endpoint = *theEndpoint;
    }
    ConnectionRequest::~ConnectionRequest()
    {
        // spdlog::info("delete ConnectionRequest");
        if (netDataReader.netPacket != nullptr)
        {
            NetPacketPool::GetInstance().recycleOrDelete(netDataReader.netPacket);
        }
    }

    bool ConnectionRequest::tryActivate()
    {
        return (m_used.exchange(true) == false);
    }

    void ConnectionRequest::updateRequest(NetConnectRequestPacket *connRequest)
    {
        if (connRequest->connectionTime >= connectionTime)
        {
            connectionTime = connRequest->connectionTime;
            connectionNumber = connRequest->connectionNumber;
        }
    }

    NetPeerPtr ConnectionRequest::accept()
    {
        if (tryActivate() == true)
        {
            result = ConnectionRequestResult::Accept;
            return m_listener->onConnectionSolved(this, nullptr, 0);
        }
        return nullptr;
    }

    void ConnectionRequest::reject(const char *rejectData, uint32_t rejectDataLength, bool force)
    {
        if (tryActivate() == true)
        {
            result = force ? ConnectionRequestResult::RejectForce : ConnectionRequestResult::Reject;
            m_listener->onConnectionSolved(this, rejectData, rejectDataLength);
        }
    }

    void ConnectionRequest::reject()
    {
        reject(nullptr, 0, false);
    }


} // namespace LiteNetLib
